
Imports System
Imports System.Data.SqlClient
Imports _2bMatesSite.Common


Namespace _2bMatesSite.BO
	Public Class FriendshipException
		Inherits Exception
		Public Sub New(ByVal msg As String)
			MyBase.New(msg)
		End Sub
	End Class

	''' <summary>
	''' Represents a direct friend with a delayed User object loading.
	''' </summary>
	Public Class DirectFriend
		Implements IComparable
		Protected userID As Integer
		Protected friendID As Integer
		Protected inviterID As Integer
		Protected [friend] As User
		Protected attempts As Integer
		Protected status As InviteStatus
		' 0-initial; 1-accepted; 2-rejected
		' -1 - not yet initiated
		Protected isDirectFriendship As Boolean
		' used to create direct friendship without confirmation
		Protected dateAdded As DateTime
		Protected isOnline As Integer
		Protected Shared m_sSortBy As String = String.Empty

		' Direct friend statuses
		Public Enum InviteStatus
			dsNonExisting = -1
			dsInitial = 0
			dsAccepted = 1
			dsRejected = 2
		End Enum

		Public Sub New(ByVal userID As Integer, ByVal friendID As Integer)
			Me.userID = userID
			Me.friendID = friendID
			inviterID = userID
			[friend] = Nothing
			attempts = 0
			status = InviteStatus.dsNonExisting
			isDirectFriendship = False
			dateAdded = DateTime.Now
			isOnline = Constants.ONLINE_STAUS_OFF
			m_sSortBy = Constants.FRIENDS_SORT_BY_NAME
		End Sub


		Public Sub New(ByVal userID As Integer, ByVal friendID As Integer, ByVal isJoin As Boolean)
			Me.userID = userID
			Me.friendID = friendID
			inviterID = userID
			[friend] = Nothing
			attempts = 0

			If isJoin Then
				status = InviteStatus.dsAccepted
				isDirectFriendship = True
			Else
				status = InviteStatus.dsNonExisting
				isDirectFriendship = False
			End If
			dateAdded = DateTime.Now
			isOnline = Constants.ONLINE_STAUS_OFF
			m_sSortBy = Constants.FRIENDS_SORT_BY_NAME
		End Sub


		Public Sub New(ByVal userID As Integer, ByVal dr As SqlDataReader)
			Me.userID = userID
			LoadFromReader(dr)
		End Sub

		Public Overloads Overrides Function GetHashCode() As Integer
			Return friendID
		End Function

		Public ReadOnly Property UserID() As Integer
			Get
				Return userID
			End Get
		End Property

		Public ReadOnly Property FriendID() As Integer
			Get
				Return friendID
			End Get
		End Property

		Public ReadOnly Property InviterID() As Integer
			Get
				Return inviterID
			End Get
		End Property

		' Returns true if the user first invited the friend
		' false if the friend invited the user
		Public ReadOnly Property IsInviter() As Boolean
			Get
				Return inviterID = userID
			End Get
		End Property

		Public ReadOnly Property IsNewInvitation() As Boolean
			Get
				Return IsInviter AndAlso attempts <= 1
			End Get
		End Property

		Public ReadOnly Property [Friend]() As User
			Get
				If [friend] = Nothing Then
					[friend] = Users.GetUserByID(friendID)
				End If
				Return [friend]
			End Get
		End Property

		Public ReadOnly Property Attempts() As Integer
			Get
				Return attempts
			End Get
		End Property

		Public ReadOnly Property Status() As InviteStatus
			Get
				Return status
			End Get
		End Property

		Public ReadOnly Property IsDirectFriendship() As Boolean
			Get
				Return isDirectFriendship
			End Get
		End Property

		Public ReadOnly Property DateAdded() As DateTime
			Get
				Return dateAdded
			End Get
		End Property

		Public Property IsOnline() As Integer
			Get
				Return isOnline
			End Get

			Set
				isOnline = value
			End Set
		End Property

		Public Shared Property SortBy() As String
			Get
				Return m_sSortBy
			End Get
			Set
				m_sSortBy = value
			End Set
		End Property

		Public Sub Save()
			If status = InviteStatus.dsNonExisting Then
				DoDelete()
			Else
				DoSave()
			End If
		End Sub

		Public Sub Delete()
			' Attempt to delete the friendship/invitation, but leave it to the
			' state machine first to avaluate
			DecrementStatus()
			Save()
		End Sub

		' The following two methods
		' implement the invitations state machine
		Public Function IncrementStatus() As InviteStatus
			Select Case status
				Case InviteStatus.dsNonExisting
					' it's a fresh new invitation
					If Not IsInviter Then
						Throw New FriendshipException("Only acting as an inviter would enable creation of a new invitation.")
					End If
					status = InviteStatus.dsInitial
					System.Math.Max(System.Threading.Interlocked.Increment(attempts),attempts - 1)
					Exit Select
				Case InviteStatus.dsInitial

					If Not IsInviter Then
						status = InviteStatus.dsAccepted
					End If
					dateAdded = DateTime.Now
					Exit Select
				Case InviteStatus.dsAccepted

					Throw New FriendshipException("Invitation was already accepted.")
				Case InviteStatus.dsRejected

					If IsInviter Then
						' another invite attempts
						If attempts >= Constants.FRIENDSHIP_MAX_REQUESTS Then
							Throw New FriendshipException("Can not request friendship more than " + Constants.FRIENDSHIP_MAX_REQUESTS.ToString() + " time(s).")
						End If

						status = InviteStatus.dsInitial
						System.Math.Max(System.Threading.Interlocked.Increment(attempts),attempts - 1)
					Else
						' accept the invitation
						status = InviteStatus.dsAccepted
					End If
					Exit Select
			End Select

			Return status
		End Function

		Public Function DecrementStatus() As InviteStatus
			Select Case status
				Case InviteStatus.dsNonExisting
					Throw New FriendshipException("Can not reject a non-existing invitation.")
				Case InviteStatus.dsInitial

					If Not IsInviter Then
						status = InviteStatus.dsRejected
						' the invited rejects the invitation
					End If
					Exit Select
				Case InviteStatus.dsAccepted

					' cancel friendship
					status = InviteStatus.dsNonExisting
					Exit Select
				Case InviteStatus.dsRejected

					Throw New FriendshipException("Invitation already rejected.")
			End Select

			Return status
		End Function

		'
		' Private and protected methods
		'

		Protected Sub DoSave()
			Dim conn As SqlConnection = DBManager.GetUnmanagedConnection()
			Dim tran As SqlTransaction = conn.BeginTransaction()

			Try
				Dim cmd As SqlCommand = Nothing

				If IsDirectFriendship Then
					cmd = New SqlCommand("INSERT INTO DirectFriends (UID, FriendUID, Attempts, Status, [Date]) VALUES (@UID, @FriendUID, @Attempts, @Status, @Date)")
				Else
					If IsNewInvitation Then
						cmd = New SqlCommand("INSERT INTO DirectFriends (UID, FriendUID, Attempts, Status, [Date]) VALUES (@UID, @FriendUID, @Attempts, @Status, @Date)")
					Else
						If IsInviter Then
							cmd = New SqlCommand("UPDATE DirectFriends SET Attempts=@Attempts, Status=@Status, [Date]=@Date WHERE UID=@UID AND FriendUID=@FriendUID")
						Else
							cmd = New SqlCommand("UPDATE DirectFriends SET Attempts=@Attempts, Status=@Status, [Date]=@Date WHERE UID=@FriendUID AND FriendUID=@UID")
						End If
					End If
				End If

				cmd.Connection = conn
				cmd.Transaction = tran

				cmd.Parameters.Add("@UID", userID)
				cmd.Parameters.Add("@FriendUID", friendID)
				cmd.Parameters.Add("@Attempts", attempts)
				cmd.Parameters.Add("@Status", DirectCast(status, Integer))
				cmd.Parameters.Add("@Date", dateAdded)

				cmd.ExecuteNonQuery()

				tran.Commit()
			Catch e As Exception
				tran.Rollback()
				Throw e
			Finally
				conn.Close()
			End Try

			Select Case status
				Case InviteStatus.dsInitial
					Users.SendFriendshipRequestNotification(Me)
					Exit Select
				Case InviteStatus.dsAccepted
					Users.SendNewFriendshipNotification(Me)
					Exit Select
			End Select
		End Sub

		Protected Sub DoDelete()
			Dim conn As SqlConnection = DBManager.GetUnmanagedConnection()

			Try
				Dim cmd As New SqlCommand("DELETE FROM DirectFriends WHERE (UID=@UID AND FriendUID=@FriendUID) OR (FriendUID=@UID AND UID=@FriendUID)")
				cmd.Connection = conn

				cmd.Parameters.Add("@UID", userID)
				cmd.Parameters.Add("@FriendUID", friendID)

				cmd.ExecuteNonQuery()

				attempts = -1
				status = InviteStatus.dsNonExisting
			Finally
				conn.Close()
			End Try
		End Sub

		Protected Sub LoadFromReader(ByVal dr As SqlDataReader)
			Dim userID As Integer = dr.GetInt32(dr.GetOrdinal("UID"))
			Dim friendID As Integer = dr.GetInt32(dr.GetOrdinal("FriendUID"))

			If userID = Me.userID Then
				Me.friendID = friendID
			Else
				Me.friendID = userID
			End If
			Me.inviterID = userID

			Me.attempts = dr.GetInt32(dr.GetOrdinal("Attempts"))
			Me.status = DirectCast(dr.GetInt32(dr.GetOrdinal("Status")), InviteStatus)
			Me.dateAdded = dr.GetDateTime(dr.GetOrdinal("Date"))
			isOnline = Constants.ONLINE_STAUS_OFF
			m_sSortBy = Constants.FRIENDS_SORT_BY_NAME
		End Sub

		Public Function CompareTo(ByVal obj As Object) As Integer
			If Not (TypeOf obj Is DirectFriend) Then
				Throw New InvalidCastException("Cannot cast to Direct friend")
			End If

			Dim df As DirectFriend = DirectCast(obj, DirectFriend)
			Select Case m_sSortBy
				Case Constants.FRIENDS_SORT_BY_NAME
					Return Me.[Friend].Name.CompareTo(df.[Friend].Name)
				Case Constants.FRIENDS_SORT_BY_GENDER
					Return (-Me.[Friend].Gender.CompareTo(df.[Friend].Gender))
				Case Constants.FRIENDS_SORT_BY_MOST_RECENT
					' minus due to values of male/female - need female to be first
					Return df.dateAdded.CompareTo(Me.dateAdded)
				Case Constants.FRIENDS_SORT_BY_AGE
					'this.dateAdded.CompareTo(df.dateAdded);
					'return (-this.Friend.RegistrationDate.CompareTo(df.Friend.RegistrationDate));
					Return (-Me.[Friend].BirthDay.CompareTo(df.[Friend].BirthDay))
				Case Constants.FRIENDS_SORT_BY_ONLINE
					Return (-Me.IsOnline.CompareTo(df.IsOnline))
				Case Else
					goto case Constants.FRIENDS_SORT_BY_NAME
			End Select
		End Function
	End Class
End Namespace






